#include "wxsdrawingwindow.h"

#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/dcclient.h>
#include <wx/dcscreen.h>
#include <wx/dcmemory.h>
#include <wx/dcbuffer.h>
#include <wx/app.h>

#include <manager.h>
#include <logmanager.h>
#include <prep.h>

namespace {

  const int wxEVT_FETCH_SEQUENCE = wxNewEventType();


  const int RefreshTimerId = wxNewId();


  enum RepaintDelayType {
    None,
    Yield,
    TimerFast,
    TimerNormal
  };

  inline RepaintDelayType GetDelayType() {
    return TimerNormal;
  }
}

BEGIN_EVENT_TABLE( wxsDrawingWindow, wxScrolledWindow )
  EVT_PAINT( wxsDrawingWindow::OnPaint )
  EVT_ERASE_BACKGROUND( wxsDrawingWindow::OnEraseBack )
END_EVENT_TABLE()

wxsDrawingWindow::wxsDrawingWindow( wxWindow* Parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ):
  wxScrolledWindow( Parent, id, pos, size, style, name ),
  m_Bitmap( 0 ),
  m_IsBlockFetch( false ),
  m_DuringFetch( false ),
  m_DuringChangeCnt( 0 ),
  m_LastSizeX( 0 ),
  m_LastSizeY( 0 ),
  m_LastVirtX( 0 ),
  m_LastVirtY( 0 ),
  m_WasContentChanged( false ),
  m_IsDestroyed( false ),
  m_RefreshTimer( this, RefreshTimerId ) {
  Connect( -1, wxEVT_FETCH_SEQUENCE, ( wxObjectEventFunction )&wxsDrawingWindow::OnFetchSequence );
  Connect( RefreshTimerId, wxEVT_TIMER, ( wxObjectEventFunction )&wxsDrawingWindow::OnRefreshTimer );
  SetScrollbars( 5, 5, 1, 1, 0, 0, true );
}

wxsDrawingWindow::~wxsDrawingWindow() {
  m_IsDestroyed = true;
  delete m_Bitmap;
  m_Bitmap = 0;
}

void wxsDrawingWindow::BeforeContentChanged() {
  m_DuringChangeCnt++;
}

void wxsDrawingWindow::AfterContentChanged() {
  if( !--m_DuringChangeCnt ) {
    m_WasContentChanged = true;
    wxSize Size = GetVirtualSize();
    delete m_Bitmap;
    m_Bitmap = new wxBitmap( Size.GetWidth(), Size.GetHeight() );
    int X, Y;
    CalcScrolledPosition( 0, 0, &X, &Y );
    StartFetchingSequence();
  }
}

void wxsDrawingWindow::OnPaint( wxPaintEvent& event ) {
  if( !m_DuringFetch ) {
    wxPaintDC PaintDC( this );
    PrepareDC( PaintDC );
    if( m_IsBlockFetch || NoNeedToRefetch() ) {
      if( m_Bitmap ) {
        wxBitmap BmpCopy = m_Bitmap->GetSubBitmap( wxRect( 0, 0, m_Bitmap->GetWidth(), m_Bitmap->GetHeight() ) );
        wxBufferedDC DC( &PaintDC, BmpCopy, wxBUFFER_VIRTUAL_AREA );
        PaintExtra( &DC );
      }
    } else
    { StartFetchingSequence(); }
  } else {
    event.Skip();
  }
}

void wxsDrawingWindow::OnEraseBack( wxEraseEvent& event ) {
  if( m_DuringFetch ) {
    event.Skip();
  }
}

void wxsDrawingWindow::StartFetchingSequence() {
  if( m_DuringFetch ) {
    return;
  }
  m_DuringFetch = true;
  wxCommandEvent event( wxEVT_FETCH_SEQUENCE, GetId() );
  event.SetEventObject( this );
  GetEventHandler()->AddPendingEvent( event );
}

void wxsDrawingWindow::OnFetchSequence( M_Code_unused wxCommandEvent& event ) {
  if( m_IsDestroyed ) {
    return;
  }
  ShowChildren();
  Refresh();
  Update();
  switch( GetDelayType() ) {
    case None:
      FetchSequencePhase2();
      break;
    case Yield:
      Manager::Yield();
      FetchSequencePhase2();
      break;
    case TimerNormal:
      m_RefreshTimer.Start( 50, true );
      break;
    case TimerFast:
      m_RefreshTimer.Start( 1, true );
      break;
    default:
      break;
  }
}

void wxsDrawingWindow::OnRefreshTimer( M_Code_unused wxTimerEvent& event ) {
  FetchSequencePhase2();
}

void wxsDrawingWindow::FetchSequencePhase2() {
  if( m_IsDestroyed ) {
    return;
  }
  FetchScreen();
  ScreenShootTaken();
  HideChildren();
  m_DuringFetch = false;
}

void wxsDrawingWindow::FetchScreen() {
  if( !m_Bitmap ) {
    return;
  }
  wxClientDC DC( this );
  wxMemoryDC DestDC;
  int X = 0, Y = 0;
  int DX = 0, DY = 0;
  CalcUnscrolledPosition( 0, 0, &DX, &DY );
  DestDC.SelectObject( *m_Bitmap );
  DestDC.Blit( DX, DY, GetSize().GetWidth(), GetSize().GetHeight(), &DC, X, Y );
  DestDC.SelectObject( wxNullBitmap );
}

void wxsDrawingWindow::FastRepaint() {
  wxClientDC ClientDC( this );
  PrepareDC( ClientDC );
  wxBitmap BmpCopy = m_Bitmap->GetSubBitmap( wxRect( 0, 0, m_Bitmap->GetWidth(), m_Bitmap->GetHeight() ) );
  wxBufferedDC DC( &ClientDC, BmpCopy, wxBUFFER_VIRTUAL_AREA );
  PaintExtra( &DC );
}

void wxsDrawingWindow::ShowChildren() {
  wxWindowList& Children = GetChildren();
  for( size_t i = 0; i < Children.GetCount(); i++ ) {
    Children[i]->Show();
  }
}

void wxsDrawingWindow::HideChildren() {
  wxWindowList& Children = GetChildren();
  for( size_t i = 0; i < Children.GetCount(); i++ ) {
    Children[i]->Hide();
  }
}

bool wxsDrawingWindow::NoNeedToRefetch() {
  if( wxTheApp && !wxTheApp->IsActive() ) {
    m_WasContentChanged = true;
    return true;
  }
  for( wxWindow* Window = this; Window; Window = Window->GetParent() ) {
    if( !Window->IsEnabled() || !Window->IsShown() ) {
      m_WasContentChanged = true;
      return true;
    }
  }
  int NewSizeX = 0, NewSizeY = 0;
  int NewVirtX = 0, NewVirtY = 0;
  GetClientSize( &NewSizeX, &NewSizeY );
  GetViewStart( &NewVirtX, &NewVirtY );
  if( m_WasContentChanged ||
      NewSizeX != m_LastSizeX ||
      NewSizeY != m_LastSizeY ||
      NewVirtX != m_LastVirtX ||
      NewVirtY != m_LastVirtY ) {
    m_WasContentChanged = false;
    m_LastSizeX = NewSizeX;
    m_LastSizeY = NewSizeY;
    m_LastVirtX = NewVirtX;
    m_LastVirtY = NewVirtY;
    return false;
  }
  return true;
}

bool wxsDrawingWindow::Destroy() {
  m_IsDestroyed = true;
  return wxScrolledWindow::Destroy();
}
